package com.ljx.itbbs.server.article.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.ljx.itbbs.common.constant.CacheConstants;
import com.ljx.itbbs.common.domain.entity.UserEntity;
import com.ljx.itbbs.common.exception.BusinessException;
import com.ljx.itbbs.common.utils.RedisUtil;
import com.ljx.itbbs.server.article.dao.ArticleCategoryDao;
import com.ljx.itbbs.server.article.dao.ArticleDao;
import com.ljx.itbbs.server.article.domain.entity.ArticleCategoryUserRelationEntity;
import com.ljx.itbbs.server.article.domain.vo.resp.ArticleCategoryResp;
import com.ljx.itbbs.server.article.service.ArticleCategoryService;
import com.ljx.itbbs.server.article.service.ArticleCategoryUserRelationService;
import com.ljx.itbbs.server.mall.service.lock.CustomRedisLock;
import com.ljx.itbbs.server.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class ArticleCategoryServiceImpl implements ArticleCategoryService {

    @Autowired
    private ArticleCategoryDao articleCategoryDao;

    @Autowired
    private UserService userService;

    @Autowired
    private ArticleCategoryUserRelationService articleCategoryUserRelationService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public List<ArticleCategoryResp> getAllCategory() {
        String key = CacheConstants.getKey(CacheConstants.ARTICLE_CATEGORY);
        String categoryStr = RedisUtil.get(key);
        if(StrUtil.isNotBlank(categoryStr)){
            return JSONUtil.toList(categoryStr, ArticleCategoryResp.class);
        }
        // 若是第一次进来为null，跳过
        if (!Objects.isNull(categoryStr)){
            return new ArrayList<>();
        }
        List<ArticleCategoryResp> allCategory = getLockCategory(key);

        return allCategory;
    }

    @Override
    public void addArticleCategoryNum(Long articleCategoryId) {
        articleCategoryDao.addArticleCategoryNum(articleCategoryId);
    }


    /**
     * 缓存空对象防止缓存穿透
     * 使用互斥锁防止缓存击穿
     * @return
     */
    public List<ArticleCategoryResp> getLockCategory(String key){
        List<ArticleCategoryResp> res;
        CustomRedisLock redisLock = new CustomRedisLock(CacheConstants.ARTICLE_CATEGORY_LOCK, stringRedisTemplate);
        try {
            // 使用互斥锁防止缓存击穿
            Boolean isLock = redisLock.tryLock(CacheConstants.ARTICLE_CATEGORY_LOCK_TIME, TimeUnit.SECONDS);
            if (!isLock){
                // 休眠10ms后继续调用
                Thread.sleep(10L);
                return this.getAllCategory();
            }
            res = articleCategoryDao.getAllCategory();
            if (CollUtil.isEmpty(res)){
                // 存入空对象，防止缓存穿透
                RedisUtil.set(key, "", CacheConstants.ARTICLE_CATEGORY_EXPIRE, TimeUnit.HOURS);
            } else {
                // 存入Redis缓存
                RedisUtil.set(key, res, CacheConstants.ARTICLE_CATEGORY_EXPIRE, TimeUnit.HOURS);
            }
        } catch (Exception e){
            throw new BusinessException(e.getMessage());
        } finally {
            redisLock.unLock();
        }

        return res;
    }

    /**
     * getAllCategory方法第一版本
     * @return
     */
    public List<ArticleCategoryResp> historyFun(){
        List<ArticleCategoryResp> allCategory = articleCategoryDao.getAllCategory();
        if (CollUtil.isEmpty(allCategory)){
            return new ArrayList<>();
        }
        List<Long> articleCategoryIds = allCategory.stream().map(ArticleCategoryResp::getId).collect(Collectors.toList());
        List<ArticleCategoryUserRelationEntity> relationList = articleCategoryUserRelationService.getByArticleCategoryIds(articleCategoryIds);
        if (CollUtil.isEmpty(relationList)){
            return allCategory;
        }
        Map<Long, List<ArticleCategoryUserRelationEntity>> relationMap = relationList
                .stream()
                .collect(Collectors.groupingBy(ArticleCategoryUserRelationEntity::getArticleCategoryId));
        Set<Long> uIds = relationList
                        .stream()
                        .map(ArticleCategoryUserRelationEntity::getUserId)
                        .collect(Collectors.toSet());
        Map<Long, UserEntity> userMap = userService.getUserInfoByIds(new ArrayList<>(uIds));

        return allCategory.stream()
                .map(item -> {
                    List<UserEntity> needUserList = relationMap.get(item.getId())
                            .stream().
                            map(uItem -> userMap.get(uItem.getUserId()))
                            .collect(Collectors.toList());
                    item.setUserList(BeanUtil.copyToList(needUserList, ArticleCategoryResp.UserInfo.class));
                    return item;
                }).collect(Collectors.toList());
    }
}
